/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.ui.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

import javax.swing.JOptionPane;
import javax.swing.table.AbstractTableModel;

import who.tmap.bo.AttrDef;
import who.tmap.bo.Attrs;
import who.tmap.bo.impl.BizObjectFactory;
import who.tmap.common.TMapException;
import who.tmap.toolkit.impl.AttrHelper;
import who.tmap.ui.util.jsorttable.ColumnComparator;
import who.tmap.ui.util.jsorttable.SortTableModel;

/**
 * A Data Model to manage thematic map data JTable
 * 
 * @author stl
 */
public class DataTableDataModel extends AbstractTableModel 
							    implements SortTableModel {

    private AttrHelper attrHelper =new AttrHelper();
    private MiscUIHelper miscUIHelper =new MiscUIHelper();

    private Attrs attrs;
    private ArrayList<AttrDef> header = new ArrayList<AttrDef>();
    @SuppressWarnings("unchecked")
	private ArrayList data = new ArrayList();
    private Map<String, String> dsProperties =new TreeMap<String, String>(); 
    
    private boolean dirty =false;

    public boolean isDirty() {
		return dirty;
	}

	public void setDirty(boolean dirty) {
		if ( attrs != null ) {
			attrs.setDirty( dirty );
		}
		this.dirty = dirty;
	}

	/**
     * Use the table object to build arrays for the swing table model
     * 
     * @param attrs
     */
    @SuppressWarnings("unchecked")
	public void setAttrs(Attrs attrs) {

    	this.attrs =attrs;
    	
    	if ( attrs != null ) {
    		header =(ArrayList<AttrDef>)attrs.getHeader();
    	} else {
    		header =null;
    	}
    	
        if ( header == null ) {
            header = new ArrayList<AttrDef>();
        }
        
        if ( attrs != null ) {
        	data =(ArrayList)attrs.getRows();
        } else {
        	data =null;
        }
        
        if ( data == null ) {
            data =new ArrayList();
        }
        
        if ( attrs != null ) {
        	dsProperties =attrs.getDsProperties();
        }
        
        setDirty( false );
        
        fireTableStructureChanged();

    }
    
    @SuppressWarnings("unchecked")
	public Attrs getAttrs() {
        Attrs attrs =null;
        try {
            attrs = (Attrs)BizObjectFactory.getInstance().get( BizObjectFactory.ATTRS );
        } catch (TMapException e) {
            new MiscUIHelper().displayErrorMessage( null, e );
        }
        attrs.setHeader( header );
        attrs.setRows( data );
        
        attrs.setDsProperties( dsProperties );
        
        attrs.setDirty( isDirty() );
        
        return attrs;
    }

    public int getColumnCount() {
        return header.size();

    }

    public int getRowCount() {
        return data.size();
    }

    public String getColumnName(int col) {
        AttrDef attrDef =(AttrDef)header.get( col );
        return attrDef.getName();
    }

    @SuppressWarnings("unchecked")
	public Object getValueAt(int row, int col) {
        return ((ArrayList)data.get( row )).get( col );
    }

    public boolean isCellEditable(int row, int col) {
        return true;
    }

    /**
     * Do some type conversion based on column type
     */
    @SuppressWarnings("unchecked")
	public void setValueAt(Object value, int row, int col) {

        AttrDef attrDef =(AttrDef)header.get( col );
        Object newVal =null;
        try {
            newVal = attrHelper.getValueFromString( (String)value, attrDef.getType() );
            if ( "".equals( value ) && newVal instanceof Number ) {
            	newVal =null;
            }
        } catch( Exception e ) {
            newVal =value;
        }
            
        ArrayList theRow =(ArrayList)data.get( row );
        theRow.set( col, newVal );
        
        setDirty( true );
        
        fireTableCellUpdated(row, col);
    }

    @SuppressWarnings("unchecked")
	public int insertRowAt(int idx ) {
    	int rowInsertedAt =idx;
        ArrayList newRow =new ArrayList();
        for( int ct =0; ct < header.size(); ct++ ) {
            Object emptyVal;
            try {
                AttrDef attrDef =(AttrDef)header.get( ct );
                emptyVal =attrHelper.getNewInstanceFromClassName( attrDef.getType() );
                if ( emptyVal instanceof Number ) {
                	emptyVal =null;
                }
            } catch (Exception e) {
                miscUIHelper.displayErrorMessage( null, e );
                emptyVal =new String();
            }
            
            newRow.add( emptyVal ); 
        }
        
        if ( idx != -1 ) {
            data.add( idx, newRow );
            fireTableRowsInserted( idx, idx ); 
        } else {
            data.add( newRow );
            rowInsertedAt =data.size()-1;
            fireTableRowsInserted( data.size() -1, data.size() -1 );
        }
        
        setDirty( true );        
        
        fireTableDataChanged();
        
        return rowInsertedAt;
    }

    @SuppressWarnings("unchecked")
	public void insertColAt(int idx, String name, String type ) {

        AttrDef attrDef =null;
        
        try {
            attrDef = (AttrDef)BizObjectFactory.getInstance().get( BizObjectFactory.ATTR_DEF );
        } catch (TMapException e1) {
            miscUIHelper.displayErrorMessage( null, e1 );
        }
        
        attrDef.setName( name );
        attrDef.setType( type );
        
        if ( idx != -1 ) {
            header.add( idx, attrDef );
        } else {
            header.add( attrDef );
        }
        
        Object val =null;
        
        Iterator it =data.iterator();
        while( it.hasNext() ) {
            ArrayList row =(ArrayList)it.next();
            
            try {
                val =attrHelper.getNewInstanceFromClassName( type );
                if ( val instanceof Number ) {
                	val =null;
                }                
            } catch (TMapException e) {
                miscUIHelper.displayErrorMessage( null, e );
            }
            
            if ( idx != -1 ) {
                row.add( idx, val );
            } else {
                row.add( val );
            }
        }
        
        setDirty( true );        
        
        fireTableStructureChanged();        
    }

    @SuppressWarnings("unchecked")
	public void removeCol(int idx) {
        header.remove( idx );
        
        Iterator it =data.iterator();
        while( it.hasNext() ) {
            ArrayList row =(ArrayList)it.next();
            row.remove( idx );
        }
        
        setDirty( true );        
        
        fireTableStructureChanged();
    }

    public void removeRows(int[] rowsIdx) {
        for (int ct = rowsIdx.length - 1; ct >= 0; ct--) {
            data.remove( rowsIdx[ct] );
            fireTableRowsDeleted( rowsIdx[ct], rowsIdx[ct] );
        }
        
        setDirty( true );
        
        fireTableDataChanged();
    }
    
    public boolean isSortable(int col) {
        return true;
    }
    
    @SuppressWarnings("unchecked")
	public void sortColumn(int col, boolean ascending) {
        try {
            Collections.sort(data, new ColumnComparator(col, ascending));
        } catch (RuntimeException e) {
            JOptionPane.showMessageDialog(null,
    	            "This column contains an invalid entry which prevents it to be correctly sorted.",
    	            "TMap error",
    	            JOptionPane.ERROR_MESSAGE);   
        }
    }
}